/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "imageknife_dispatcher.h"
#include "task_worker.h"
#include "memory_cache.h"
#include "imageknife_loader.h"
#include "task_worker.h"
#include "imageknife_internal.h"
#include "imageknife_task_internal.h"
#include "loader/imageknife_loader_internal.h"
#include "constants.h"
#include "imageknife_data_napi.h"
#include "image_data_cache_internal.h"
#include "libbase64.h"

namespace ImageKnifeC {

ImageKnifeDispatcher::ImageKnifeDispatcher()
{
}

ImageKnifeDispatcher::~ImageKnifeDispatcher()
{
    if (jobQueuePtr_ != nullptr) {
        delete jobQueuePtr_;
    }
}

void ImageKnifeDispatcher::Enqueue(std::shared_ptr<ImageKnifeRequestInternal> request)
{
    request->GetImageInfo().timeInfo.executeTime = ImageKnifeDataNapi::GetTimestamp();
    if (request->GetImageKnifeOption()->imageLoader == nullptr) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                     "Enqueue",
                     "Can't find ImageKnifeLoader");
        if (request->GetImageKnifeOption()->onLoadListener != nullptr &&
            request->GetImageKnifeOption()->onLoadListener->onLoadFailed != nullptr) {
            request->GetImageKnifeOption()->onLoadListener->onLoadFailed("Can't find ImageKnifeLoader",
                                                                         request->GetImageInfo());
        }
        return;
    }

    // 1.内存有的话直接渲染
    if (LoadSrcFromMemory(request, ImageRequestType::MAIN_SRC)) {
        return;
    }

    bool foundPlaceHolder = LoadSrcFromMemory(request, ImageRequestType::PLACE_SRC);

    // 判断是否排队
    if (executingJobMap_.size() >= maxRequests) {
        DefaultJobQueue::QueueElement mainQueueElement(request, ImageRequestType::MAIN_SRC);
        jobQueuePtr_->Add(mainQueueElement);

        if (!foundPlaceHolder && !request->IsSyncLoad()) {
            DefaultJobQueue::QueueElement placeQueueElement(request, ImageRequestType::PLACE_SRC);
            jobQueuePtr_->Add(placeQueueElement);
        }
        return;
    }

    // 启动加载主图
    ExecuteJob(ImageRequestType::MAIN_SRC, request);

    // 启动加载占位图
    if (!foundPlaceHolder && !request->IsSyncLoad()) {
        ExecuteJob(ImageRequestType::PLACE_SRC, request);
    }
}

bool ImageKnifeDispatcher::EncodeSourceBase64(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    std::string prefix = "data:image/";
    if (task->GetImageBufferFormat() == ImageFormat::GIF) {
        prefix += "gif;base64,";
    } else if (task->GetImageBufferFormat() == ImageFormat::WEBP) {
        prefix += "webp;base64,";
    } else {
        task->EchoError("Not An Animation Source (Encode Base64)");
        return false;
    }

    const char *source = nullptr;
    size_t length = 0;
    if (task->product.imageBuffer != nullptr) {
        source = reinterpret_cast<char *>(task->product.imageBuffer.get());
        length = task->product.imageLength;
    } else {
        return false;
    }

    // 编码后size增加至原本的4/3,再加上前缀和尾0
    const size_t incrementalSize = length * 4 / 3 + prefix.size() + 1;

    char *out = new char[incrementalSize];
    out[incrementalSize - 1] = 0; // 设置字符串尾0 
    strcpy(out, prefix.c_str());
    size_t outLength = 0;

    const int plainCodec = 1 << 3;
    base64_encode(source, length, out + prefix.size(), &outLength, plainCodec);
    task->product.imageDataCache = std::make_shared<ImageDataCacheInternal>(out);
    return true;
}

bool ImageKnifeDispatcher::ProduceImageCache(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    std::shared_ptr<ImageDataCacheInternal> imageDataCache;
    if (task->product.imageDataCache != nullptr) {
        // 已经构造了base64类型的缓存对象
        imageDataCache = std::static_pointer_cast<ImageDataCacheInternal>(task->product.imageDataCache);
    } else if (task->product.imageData->GetPixelmapList() != nullptr) {
        imageDataCache = std::make_shared<ImageDataCacheInternal>(task->product.imageData, task->IsAnimator());
    } else {
        task->EchoError("ImageData Unavailable (Produce ImageCache)");
        return false;
    }

    imageDataCache->type = task->GetImageBufferFormat();
    // TODO 请求数据采集：补充其他缓存数据
    task->product.imageDataCache = imageDataCache;

    return true;
}

void ImageKnifeDispatcher::LoadImageSource(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    auto request = static_cast<ImageKnifeRequestInternal*>(task->GetImageKnifeRequest().get());
    auto imageLoader = static_cast<ImageKnifeLoaderInternal*>(request->GetImageKnifeOption()->imageLoader.get());
    // 图片加载阶段
    request->GetImageInfo().errorInfo.phase = ImageKnifeDataNapi::phaseToStringMap[LoadPhase::DISPATCH];
    // 检查文件缓存
    bool loadEnd = imageLoader->LoadFromFile(task);
    bool writeFileCache = !loadEnd;

    if (request->IsDestroy() || task->IsFatalErrorHappened()) {
        return;
    }

    // 加载结束
    if (!loadEnd) {
        loadEnd = imageLoader->DownloadImage(task);
    }

    if (request->IsDestroy() || task->IsFatalErrorHappened()) {
        return;
    }

    if (!loadEnd) {
        if (task->GetLastError().empty()) {
            task->EchoError("load Image Failed! No useful error message (Loading)");
        }
        return;
    }

    // 判断图片类型,如果是普通Image组件,且图片为动图,则转换为Base64, 跳过解码与图形变换
    task->QueryImageBufferFormat();
    if (!task->IsAnimator() && task->IsAnimationImage()) {
        if (!EncodeSourceBase64(task)) {
            return;
        }
    } else {
        // 图片解码
        if (!imageLoader->DecodeImage(task) || task->IsFatalErrorHappened()) {
            if (task->GetLastError().empty()) {
                task->EchoError("Image Decode Error! No Useful Error Message (Loading), ImageFormat:" +
                                std::to_string(static_cast<int>(task->GetImageBufferFormat())));
            }
            return;
        }

        if (request->IsDestroy() || task->IsFatalErrorHappened()) {
            return;
        }

        // 图片形变, 默认没有图片形变
        TransformImage(task);

        if (request->IsDestroy() || task->IsFatalErrorHappened()) {
            return;
        }
    }

    if (!ProduceImageCache(task)) {
        return;
    }
    // 写内存缓存
    imageLoader->WriteCacheToMemory(task);

    // 写文件缓存在另外线程中，不阻塞当前图片显示，task生命周期在写缓存过程中有效
    if (writeFileCache) {
        auto func = [imageLoader](std::shared_ptr<ImageKnifeTaskInternal> task) {
            imageLoader->WriteCacheToFile(task);
        };
        TaskWorker::GetInstance()->PushTask(func, nullptr, task);
    }

    task->QueryDisplayPixelmapInfo();
    task->MarkSuccess();
}

void ImageKnifeDispatcher::ProcessCanceledRequest(std::shared_ptr<ImageKnifeRequestInternal> request,
                                                  ImageRequestType type, std::string info, bool fromQueue)
{
    if (fromQueue) {
        request->GetImageInfo().errorInfo.code = LoadPixelMapCode::QUEUE_CANCEL;
    } else {
        request->GetImageInfo().errorInfo.code = LoadPixelMapCode::LOADING_CANCEL;    
    }

    // 获取请求取消时间戳
    request->GetImageInfo().timeInfo.requestCancelTime = ImageKnifeDataNapi::GetTimestamp();
    // 任务在执行中被取消
    std::string cancelInfo = ImageKnifeTaskInternal::GetTaskInfo(type, request.get()) + info;
    OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, "ProcessCanceledRequest", "%{public}s", cancelInfo.c_str());
    auto option = request->GetImageKnifeOption();
    if (option->onLoadListener != nullptr && type == ImageRequestType::MAIN_SRC &&
        option->onLoadListener->onLoadCancel != nullptr) {
        option->onLoadListener->onLoadCancel(cancelInfo, request->GetImageInfo());
    }
}

void ImageKnifeDispatcher::ProcessSucceedRequest(std::shared_ptr<ImageKnifeRequestInternal> request,
                                                 ImageRequestType type,
                                                 std::shared_ptr<ImageKnifeTaskInternal> task)
{
    if (type != ImageRequestType::PLACE_SRC) {
        request->MarkStatusComplete();
    }
    // 显示前打印图片信息info日志
    std::string info = ImageKnifeTaskInternal::GetTaskInfo(type, request.get());
    info += task->GetDisplayPixelmapInfo();
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "Display", "%{public}s", info.c_str());

    bool triggerCallback = true;
    // preload只加载不显示
    if (!request->IsPreload()) {
        request->MarkDisplayStartTime(type, false);
        triggerCallback = DisplayImage(request, type, task->product.imageDataCache);
    }

    auto option = request->GetImageKnifeOption();
    if (option->onLoadListener != nullptr && type == ImageRequestType::MAIN_SRC &&
        option->onLoadListener->onLoadSuccess != nullptr && triggerCallback) {
        // 成功时获取请求结束时间戳
        request->GetImageInfo().timeInfo.requestEndTime = ImageKnifeDataNapi::GetTimestamp();
        // 触发onLoadSuccess回调
        option->onLoadListener->onLoadSuccess(request->GetImageInfo());
    }
}

void ImageKnifeDispatcher::ProcessFailedRequest(std::shared_ptr<ImageKnifeRequestInternal> request,
                                                ImageRequestType type, std::string errorInfo)
{
    std::string info = ImageKnifeTaskInternal::GetTaskInfo(type, request.get()) + errorInfo;
    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "DisplayImage", "%{public}s", info.c_str());
    if (request->GetImageKnifeOption()->onLoadListener != nullptr && type == ImageRequestType::MAIN_SRC &&
        request->GetImageKnifeOption()->onLoadListener->onLoadFailed != nullptr) {
        request->GetImageKnifeOption()->onLoadListener->onLoadFailed(info, request->GetImageInfo());
    }
}

void ImageKnifeDispatcher::ProcessMainSrcFailedRequest(std::shared_ptr<ImageKnifeRequestInternal> request,
                                                       std::shared_ptr<ImageKnifeTaskInternal> task)
{
    // 失败时获取请求结束时间戳
    request->GetImageInfo().timeInfo.requestEndTime = ImageKnifeDataNapi::GetTimestamp();
    // 任务执行失败
    auto option = request->GetImageKnifeOption();
    if (option->onLoadListener != nullptr && option->onLoadListener->onLoadFailed != nullptr) {
        option->onLoadListener->onLoadFailed(task->GetLastError(), request->GetImageInfo());
    }
    // 主图失败启动错误占位图
    request->MarkStatusError();
    // 有缓存不会启用task加载，finalize会在更新或析构时调用的CancelRequest时触发，与错误图未启用时一样
    if (!LoadSrcFromMemory(request, ImageRequestType::ERROR_SRC)) {
        // 如果被合并的请求的错误图不是同一张，则实际的请求数大于设置的最大并发数
        ExecuteJob(ImageRequestType::ERROR_SRC, request);
    }
}

bool ImageKnifeDispatcher::IsRequestOverdue(std::shared_ptr<ImageKnifeRequestInternal> request)
{
    if (request->IsPreload()) {
        return false;
    }

    auto imageNode = ImageKnifeInternal::GetInstance().GetImageNode(request->GetNodeId());
    if (imageNode == nullptr || imageNode->GetNodeState() == ImageKnifeNodeInternal::NodeState::Cleared ||
        imageNode->GetNodeVersion() != request->GetVersion()) {
        return true;
    }
    return false;
}

void ImageKnifeDispatcher::OnComplete(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    auto request = static_cast<ImageKnifeRequestInternal*>(task->GetImageKnifeRequest().get());

    try {
        for (DefaultJobQueue::QueueElement queueElement : executingJobMap_[task->memoryKey]) {
            if (queueElement.request->IsDestroy()) {
                // 合并的请求被取消
                ProcessCanceledRequest(queueElement.request, queueElement.type, "Request Abort. " + task->cancelInfo);
            } else if (IsRequestOverdue(queueElement.request)) {
                // 合并的请求过期
                ProcessCanceledRequest(queueElement.request, queueElement.type, "Request Overdue");
            } else if (request->IsDestroy()) {
                // 如果加载的请求被取消，被合并的请求需要重新排队进行加载
                jobQueuePtr_->Add(queueElement);
                std::string info = ImageKnifeTaskInternal::GetTaskInfo(queueElement.type, queueElement.request.get());
                OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, "DestroyedRequest",
                             "%{public}s Merged Request Enqueue Due To Task Canceled", info.c_str());
            } else if (task->IsSuccess()) {
                // 加载成功
                ProcessSucceedRequest(queueElement.request, queueElement.type, task);
            } else if (queueElement.type == ImageRequestType::MAIN_SRC) {
                // 加载失败且请求类型为主图
                ProcessMainSrcFailedRequest(queueElement.request, task);
            }
        }
    } catch (std::exception err) {
        task->FatalError("Dispatcher::OnComplete " + std::string(err.what()));
    }

    executingJobMap_.erase(task->memoryKey);
    DispatchNextJob();

    // 移除task记录
    request->RemoveTask(task->GetImageRequestType());
}

bool ImageKnifeDispatcher::DisplayImage(std::shared_ptr<ImageKnifeRequestInternal> request,
                                        ImageRequestType type,
                                        std::shared_ptr<ImageDataCache> imageData)
{
    if (type == ImageRequestType::PLACE_SRC && request->IsMainSrcComplete()) {
        // 主图已显示的情况下,不显示占位图
        return false;
    }

    auto imageNode = ImageKnifeInternal::GetInstance().GetImageNode(request->GetNodeId());
    if (imageNode != nullptr) {
        // 设置图片填充效果
        ArkUI_ObjectFit fitEnum = ARKUI_OBJECT_FIT_CONTAIN;
        switch (type) {
            case ImageRequestType::MAIN_SRC:
                fitEnum = request->GetImageKnifeOption()->objectFit;
                break;
            case ImageRequestType::PLACE_SRC:
                fitEnum = request->GetImageKnifeOption()->placeholderObjectFit;
                break;
            case ImageRequestType::ERROR_SRC:
                fitEnum = request->GetImageKnifeOption()->errorholderObjectFit;
                break;
            default:
                break;
        }
        imageNode->FitImage(static_cast<int>(fitEnum));

        // 显示图片
        auto dataCache = std::dynamic_pointer_cast<ImageDataCacheInternal>(imageData);
        if ((dataCache != nullptr) && imageNode->Display(dataCache, type == ImageRequestType::PLACE_SRC)) {
            return true;
        } else {
            ProcessFailedRequest(request, type, "ImageDataCache Unavailable (Display Image)");
        }
    }

    return false;
}

bool ImageKnifeDispatcher::LoadSrcFromMemory(std::shared_ptr<ImageKnifeRequestInternal> request,
                                             ImageRequestType type)
{
    try {
        uint64_t loadMemoryStartTime = ImageKnifeDataNapi::GetTimestamp();
        ImageSource *data = GetImageSrc(request, type);
        if (data == nullptr) {
            return false;
        }

        auto imageLoader = static_cast<ImageKnifeLoaderInternal *>(request->GetImageKnifeOption()->imageLoader.get());
        auto task = std::make_shared<ImageKnifeTaskInternal>(type, request, data);
        if (imageLoader->LoadFromMemory(task)) {
            // 请求开始时间
            request->GetImageInfo().timeInfo.requestStartTime = loadMemoryStartTime;
            // js层start回调
            if (request->GetImageKnifeOption()->onLoadListener != nullptr &&
                request->GetImageKnifeOption()->onLoadListener->onLoadStart != nullptr &&
                type == ImageRequestType::MAIN_SRC) {
                request->GetImageKnifeOption()->onLoadListener->onLoadStart(request->GetImageInfo());
            }
            if (IsRequestOverdue(request)) {
                // 过期request 不显示
                ProcessCanceledRequest(request, type, "Request Overdue");
            } else {
                // 显示前打印图片信息info日志
                task->QueryDisplayPixelmapInfo();
                ProcessSucceedRequest(request, type, task);
            }

            return true;
        }
    } catch (std::exception err) {
        std::string info = ImageKnifeTaskInternal::GetTaskInfo(type, request.get());
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                     "Dispatcher",
                     "%{public}s [Fatal Error] %{public}s (LoadSrcFromMemory)",
                     info.c_str(),
                     err.what());
    }

    return false;
}

ImageSource *ImageKnifeDispatcher::GetImageSrc(std::shared_ptr<ImageKnifeRequestInternal> request,
                                               ImageRequestType type)
{
    ImageSource *data = nullptr;
    switch (type) {
        case ImageRequestType::MAIN_SRC:
            data = &request->GetImageKnifeOption()->loadSrc;
            break;
        case ImageRequestType::PLACE_SRC:
            data = &request->GetImageKnifeOption()->placeholderSrc;
            break;
        case ImageRequestType::ERROR_SRC:
            data = &request->GetImageKnifeOption()->errorholderSrc;
            break;
        default:
            break;
    }

    if (data == nullptr || data->GetType() == DataType::UNDEFINED) {
        // 该图片类型未启用， 如果是主图缺失则会输出错误并进行错误回调
        if (type == ImageRequestType::MAIN_SRC) {
            ProcessFailedRequest(request, type, "Empty MainSrc (GetImageSrc)");    
        }
        return nullptr;
    }
    return data;
}

bool ImageKnifeDispatcher::ExecuteJob(ImageRequestType type,
                                      std::shared_ptr<ImageKnifeRequestInternal> request)
{
    if (type == ImageRequestType::MAIN_SRC) {
        // 请求开始时间
        request->GetImageInfo().timeInfo.requestStartTime = ImageKnifeDataNapi::GetTimestamp();
        // js层start回调
        if (request->GetImageKnifeOption()->onLoadListener != nullptr &&
            request->GetImageKnifeOption()->onLoadListener->onLoadStart != nullptr) {
            request->GetImageKnifeOption()->onLoadListener->onLoadStart(request->GetImageInfo());
        }
    }

    ImageSource *data = GetImageSrc(request, type);
    if (data == nullptr) {
        return false;
    }

    std::string memoryKey;
    std::shared_ptr<ImageKnifeOption> imageKnifeOption = request->GetImageKnifeOption();

    // 设置默认执行：图片url，resource 进行加载解码，图形变换
    AsyncTask func = [this](std::shared_ptr<ImageKnifeTaskInternal> task) {
        this->LoadImageSource(task);
    };

    bool isAnimator = false;
    if (!request->IsPreload()) {
        isAnimator = ImageKnifeInternal::GetInstance().GetImageNode(request->GetNodeId())->IsAnimator();
    }
    CacheKeyGenerator::MemoryKeyParams memoryKeyParams;
    memoryKeyParams.imageSource = data;
    memoryKeyParams.type = type;
    memoryKeyParams.isAnimator = isAnimator;
    memoryKeyParams.signature = request->GetImageKnifeOption()->signature;
    memoryKey = ImageKnife::GetInstance().GetCacheKeyGenerator()->GenerateMemoryKey(memoryKeyParams);

    DefaultJobQueue::QueueElement queueElement(request, type);

    if (!memoryKey.empty()) {
        if (executingJobMap_.find(memoryKey) == executingJobMap_.end()) {
            executingJobMap_.emplace(memoryKey, std::list<DefaultJobQueue::QueueElement>({std::move(queueElement)}));
        } else {
            executingJobMap_[memoryKey].emplace_back(std::move(queueElement));
            OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, "ExecuteJob", "%{public}s Request Merged",
                         ImageKnifeTaskInternal::GetTaskInfo(type, request.get()).c_str());
            return false;
        }
    }

    auto task = std::make_shared<ImageKnifeTaskInternal>(type, request, data);
    task->memoryKey = memoryKey;

    if (request->IsSyncLoad()) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "ExecuteJob", "%{public}s Start Task SyncLoad",
                     ImageKnifeTaskInternal::GetTaskInfo(type, request.get()).c_str());
        func(task);
        OnComplete(task);
    } else {
        AsyncTask completeCallback = [this](std::shared_ptr<ImageKnifeTaskInternal> task) {
            this->OnComplete(task);
        };

        if (TaskWorker::GetInstance()->PushTask(func, completeCallback, task, napi_qos_user_initiated)) {
            // 线程任务push时info日志
            OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN,
                         "ExecuteJob",
                         "%{public}s Push Task Success",
                         ImageKnifeTaskInternal::GetTaskInfo(type, request.get()).c_str());
        } else {
            auto internalRequest = std::dynamic_pointer_cast<ImageKnifeRequestInternal>(task->GetImageKnifeRequest());
            if (internalRequest != nullptr) {
                internalRequest->GetImageInfo().errorInfo.code = LoadPixelMapCode::DISPATCH_THREAD;
            }
            if (request->GetImageKnifeOption()->onLoadListener != nullptr &&
                request->GetImageKnifeOption()->onLoadListener->onLoadFailed != nullptr &&
                type == ImageRequestType::MAIN_SRC) {
                request->GetImageKnifeOption()->onLoadListener->onLoadFailed(task->GetLastError(),
                                                                             request->GetImageInfo());
            }
            request->Destroy();
            return false;
        }
    }
    return true;
}

void ImageKnifeDispatcher::TransformImage(std::shared_ptr<ImageKnifeTaskInternal> task)
{
}

// 在主线程中调用
void ImageKnifeDispatcher::CancelRequest(std::shared_ptr<ImageKnifeRequestInternal> request)
{
    request->Destroy();

    // 调用对应interceptor的cancel函数
    CancelInterceptor(request, ImageRequestType::MAIN_SRC);
    CancelInterceptor(request, ImageRequestType::PLACE_SRC);
    CancelInterceptor(request, ImageRequestType::ERROR_SRC);
}

void ImageKnifeDispatcher::CancelInterceptor(std::shared_ptr<ImageKnifeRequestInternal> request,
                                             ImageRequestType type)
{
    auto task = std::static_pointer_cast<ImageKnifeTaskInternal>(request->GetTask(type));
    if (task != nullptr) {
        auto interceptor = task->GetCurrentInterceptor();
        if (interceptor != nullptr) {
            interceptor->Cancel(task);
        }
    }
}

void ImageKnifeDispatcher::DispatchNextJob()
{
    if (executingJobMap_.size() >= maxRequests) {
        return;
    }
    while (true) {
        DefaultJobQueue::QueueElement queueElement = jobQueuePtr_->Pop();
        if (queueElement.request == nullptr) {
            break;
        } else if (queueElement.request->GetStatus() == ImageKnifeRequest::Status::PROGRESS ||
                   queueElement.request->GetStatus() == ImageKnifeRequest::Status::ERROR) {
            if (ExecuteJob(queueElement.type, queueElement.request)) { // 任务开启成功
                break;
            }
        } else if (queueElement.request->GetStatus() == ImageKnifeRequest::Status::DESTROY) {
            ProcessCanceledRequest(queueElement.request, queueElement.type, "Request Cancel From Queue", true);
        }
    }
}
} //  end of namespace